using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using xTile;
using xTile.Dimensions;
using xTile.Display;

namespace Broadsides2d
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;

        SpriteBatch spriteBatch;

        SpriteFont arial;
        SpriteFont arial2;
        SpriteFont arial3;

        public static List<Player> players;
        public static Player thisPlayer;
        public static List<NPC> npcShips;
        public static List<Cannonball> cannonballs;


        public Texture2DManager tm;
        public World world;

        int gameTimer;
        int elapseTime = 0;
        int FPS = 0;
        int frameCounter = 0;

        public static int screenX = 1599;
        public static int screenY = 900;

        private int highScore;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            //this.graphics.IsFullScreen = true;
            DisplayMode displayMode = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode;
            screenY = this.graphics.PreferredBackBufferHeight = displayMode.Height-25;
            screenX = this.graphics.PreferredBackBufferWidth = displayMode.Width-1;
            this.graphics.PreferredBackBufferFormat = displayMode.Format;


            this.graphics.SynchronizeWithVerticalRetrace = false;

            this.graphics.ApplyChanges();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            world = new World(200);

            world.mapDisplayDevice = new XnaDisplayDevice(this.Content, this.GraphicsDevice);

            restartGame();


            base.Initialize();
        }

        private void restartGame()
        {
            players = new List<Player>();
            npcShips = new List<NPC>();
            cannonballs = new List<Cannonball>();
            
            tm = new Texture2DManager();

            thisPlayer = new Player(300f, 300f, Ship.ShipType.player, 1);
            players.Add(thisPlayer);

            //thisPlayer = new Player(250f, 300f, Ship.ShipType.player, 2);
            //players.Add(thisPlayer);

            elapseTime = DateTime.Now.Second;
            gameTimer = 600;

        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            arial = Content.Load<SpriteFont>("arial");
            arial2 = Content.Load<SpriteFont>("ariel2");
            arial3 = Content.Load<SpriteFont>("arial3");

            AttackNPC.texture = Content.Load<Texture2D>("images\\npc-ship03");
            TradeNPC.texture = Content.Load<Texture2D>("images\\npc-ship02");
            Texture2D cannonballSprite = Content.Load<Texture2D>("images\\cannonball");
            Texture2DManager.land = Content.Load<Texture2D>("images\\land");
            Texture2DManager.port = Content.Load<Texture2D>("images\\port");
            Player.texture = Content.Load<Texture2D>("images\\pirate-ship01");
            Player.texture2 = Content.Load<Texture2D>("images\\pirate-ship03");

            world.map = Content.Load<Map>("Maps\\map1");
            world.map.LoadTileSheets(world.mapDisplayDevice);
            world.setupMap();

            Ship.image.Add(Content.Load<Texture2D>("images\\ship"));
            Cannonball.cannonballImage = cannonballSprite;

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {            

            // TODO: Add your update logic here

            foreach (Player p in players)
                p.Update();
            
            foreach (Port p in World.world.ports)
                p.Update();

            //foreach (NPC n in npcShips)
            for (int i = npcShips.Count-1; i >= 0; i--)
            {
                NPC n = npcShips.ElementAt(i);
                n.Update();
            }

            handleInput();
            world.update(gameTime); 

            for (int i = cannonballs.Count - 1; i >= 0; i--)
            {
                Cannonball c = cannonballs.ElementAt(i);
                c.Update();
            }

            checkGameOver();

            base.Update(gameTime);
        }

        private void calculateFPS(GameTime gameTime)
        {
            frameCounter++;
            if (elapseTime < DateTime.Now.Second || (DateTime.Now.Second == 0 && elapseTime == 59))
            {
                gameTimer--;
                FPS = frameCounter;
                frameCounter = 0;
                elapseTime = DateTime.Now.Second;
            }
        }

        private void checkGameOver()
        {
            bool end = true;
            foreach (Player p in players)
            {
                end = !(p.getLife() > 0);
                highScore = Math.Max(p.getGold(), highScore);
            }
            if (end && gameTimer > 0)
                gameTimer = -1;
            else
                if (gameTimer <= -4)
                    restartGame();
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);


            // TODO: Add your drawing code here
            //spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, Matrix.CreateScale(.5f));
            spriteBatch.Begin();

            world.draw(spriteBatch);

            foreach (Player p in players)
                p.Draw(spriteBatch); 
            foreach (Cannonball c in cannonballs)
                c.Draw(spriteBatch);
            foreach (NPC n in npcShips)
                n.Draw(spriteBatch);

            //foreach (Port p in World.world.ports) //for debuging A*
            //{
            //    foreach (List<Tile> list in p.routes)
            //    {
            //        if (list != null)
            //        {
            //            foreach (Tile t in list)
            //            {
            //                t.Draw(spriteBatch);
            //            }
            //        }
            //    }
            //}

            calculateFPS(gameTime);

            //DrawDebugText();

            if (gameTimer < 0)
            {
                drawGameOver();
            }

            drawHud();

            spriteBatch.End();


            base.Draw(gameTime);
        }

        private void handleInput()
        {
            KeyboardState keyState = Keyboard.GetState();
            GamePadState padState;

            // Allows the game to exit
            foreach(Player p in players)
            {
                if (p.getPlayerNum() == 1)
                {
                    if(p.getSpeed() > p.getBaseSpeed()) 
                        p.setSpeed(p.getSpeed() - .01f);
                    else if (p.getSpeed() < p.getBaseSpeed()) 
                        p.setSpeed(p.getSpeed() + .01f);

                    padState = GamePad.GetState(PlayerIndex.One);
                    if (padState.ThumbSticks.Left.X != 0)
                        p.rotateShip(padState.ThumbSticks.Left.X * (float)(Math.PI / 180));
                    else
                    {
                        if (keyState.IsKeyDown(Keys.W))
                            if(p.getSpeed() < p.getSpeedCapTop()) 
                                p.setSpeed(p.getSpeed() + .02f);
                        if (keyState.IsKeyDown(Keys.A))
                            p.rotateShip(-(float)(Math.PI / 180));
                        if (keyState.IsKeyDown(Keys.D))
                            p.rotateShip((float)(Math.PI / 180));
                        if (keyState.IsKeyDown(Keys.S))
                            if (p.getSpeed() > p.getSpeedCapBot())
                                p.setSpeed(p.getSpeed() - .02f);

                    }

                    if (keyState.IsKeyDown(Keys.E) || padState.Triggers.Right > 0)
                        p.fireCannon(Ship.CannonSide.right);
                    if (keyState.IsKeyDown(Keys.Q) || padState.Triggers.Left > 0)
                        p.fireCannon(Ship.CannonSide.left);
                    if (keyState.IsKeyDown(Keys.Z) || padState.IsButtonDown(Buttons.RightShoulder))
                        thisPlayer = p;
                    if (keyState.IsKeyDown(Keys.Escape) || padState.IsButtonDown(Buttons.Back))
                        this.Exit();
                }
                else if (p.getPlayerNum() == 2)
                {
                    if (p.getSpeed() > p.getBaseSpeed())
                        p.setSpeed(p.getSpeed() - .01f);
                    else if (p.getSpeed() < p.getBaseSpeed())
                        p.setSpeed(p.getSpeed() + .01f);

                    padState = GamePad.GetState(PlayerIndex.Two);
                    if (padState.ThumbSticks.Left.X != 0)
                        p.rotateShip(padState.ThumbSticks.Left.X * (float)(Math.PI / 180));
                    else
                    {
                        if (keyState.IsKeyDown(Keys.I))
                            if (p.getSpeed() < p.getSpeedCapTop())
                                p.setSpeed(p.getSpeed() + .02f);
                        if (keyState.IsKeyDown(Keys.J))
                            p.rotateShip(-(float)(Math.PI / 180));
                        if (keyState.IsKeyDown(Keys.L))
                            p.rotateShip((float)(Math.PI / 180));
                        if (keyState.IsKeyDown(Keys.K))
                            if (p.getSpeed() > p.getSpeedCapBot())
                                p.setSpeed(p.getSpeed() - .02f);
                    }
                   

                    if (keyState.IsKeyDown(Keys.U) || padState.Triggers.Right > 0)
                        p.fireCannon(Ship.CannonSide.right);
                    if (keyState.IsKeyDown(Keys.P) || padState.Triggers.Left > 0)
                        p.fireCannon(Ship.CannonSide.left);
                    if (keyState.IsKeyDown(Keys.N) || padState.IsButtonDown(Buttons.RightShoulder))
                        thisPlayer = p;
                    if (keyState.IsKeyDown(Keys.Escape) || padState.IsButtonDown(Buttons.Back))
                        this.Exit();
                }
            }
        }

        private void DrawDebugText()
        {
            Player player = players.ElementAt(0);

            spriteBatch.DrawString(arial, 
                player.getCurrentCooldown(Ship.CannonSide.left) + ", " +
                player.getCurrentCooldown(Ship.CannonSide.right) + "\n" +
                FPS + "\n" +
                player.getGold() + "g\n" +
                player.getLife() + "\n" +
                player.getSpeed() + "\n" +
                gameTimer
                , new Vector2(5, 5), Color.White);
        }

        private void drawHud()
        {
            foreach (Player p in players)
            {
                spriteBatch.DrawString(arial3, "Gold: " + players.ElementAt(p.getPlayerNum()-1).getGold(), new Vector2(0, 20*(p.getPlayerNum()-1)), Color.White);
                spriteBatch.DrawString(arial3, "Life: " + players.ElementAt(p.getPlayerNum() - 1).getLife(), new Vector2(screenX / 2 - 10, 20 * (p.getPlayerNum() - 1)), Color.White);
            }
            spriteBatch.DrawString(arial3, "Time: " + gameTimer, new Vector2(screenX - 100, 0), Color.White);
        }

        private void drawGameOver()
        {
            spriteBatch.DrawString(arial2, "GAME OVER\n    " + highScore, new Vector2(screenX / 3, screenY / 3), Color.White);
        }

        public static Vector2 recenter(float x, float y)
        {
            float tempx = x - thisPlayer.getX() + screenX/2;
            float tempy = y - thisPlayer.getY() + screenY/2;
            

            return new Vector2(tempx, tempy);
            //return new Vector2(x, y);
        }
    }
}
